package com.wander.rep.server;

import static com.wander.rep.server.CommonUtilities.DEFAULT_SERVER_URL;
import static com.wander.rep.server.CommonUtilities.TAG;
import static com.wander.rep.server.CommonUtilities.displayMessage;

import com.google.android.gcm.GCMRegistrar;
import com.wander.rep.R;
import com.wander.rep.patient.Issue;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;

public final class ServerUtilities {

	private static final int MAX_ATTEMPTS = 5;
	private static final int BACKOFF_MILLI_SECONDS = 2000;
	private static final Random random = new Random();

	/**
	 * Register this account/device pair within the server.
	 *
	 * @return whether the registration succeeded or not.
	 */
	public static boolean register(final Context context, final String regId, final String regName, final String regEmail) {
		
		Log.i(TAG, "registering device (regId = " + regId + ")");
		String serverUrl = DEFAULT_SERVER_URL + "/register";
		Map<String, String> params = new HashMap<String, String>();
		params.put("regId", regId);
		params.put("regName", regName);
		params.put("regEmail", regEmail);
		
		long backoff = BACKOFF_MILLI_SECONDS + random.nextInt(1000);
		// Once GCM returns a registration id, we need to register it in the
		// demo server. As the server might be down, we will retry it a couple
		// times.
		for (int i = 1; i <= MAX_ATTEMPTS; i++) {
			Log.d(TAG, "Attempt #" + i + " to register");
			try {
				displayMessage(context, context.getString(R.string.server_registering, i, MAX_ATTEMPTS));
				post(serverUrl, params);
				GCMRegistrar.setRegisteredOnServer(context, true);
				String message = context.getString(R.string.server_registered);
				CommonUtilities.displayMessage(context, message);
				return true;
			} catch (IOException e) {
				// Here we are simplifying and retrying on any error; in a real
				// application, it should retry only on unrecoverable errors
				// (like HTTP error code 503).
				Log.e(TAG, "Failed to register on attempt " + i, e);
				if (i == MAX_ATTEMPTS) {
					break;
				}
				try {
					Log.d(TAG, "Sleeping for " + backoff + " ms before retry");
					Thread.sleep(backoff);
				} catch (InterruptedException e1) {
					// Activity finished before we complete - exit.
					Log.d(TAG, "Thread interrupted: abort remaining retries!");
					Thread.currentThread().interrupt();
					return false;
				}
				// increase backoff exponentially
				backoff *= 2;
			}
		}

		String message = context.getString(R.string.server_register_error,
				MAX_ATTEMPTS);
		CommonUtilities.displayMessage(context, message);
		return false;
	}

	/**
	 * Unregister this account/device pair within the server.
	 */
	public static void unregister(final Context context, final String regId) {
		Log.i(TAG, "unregistering device (regId = " + regId + ")");
		String serverUrl = DEFAULT_SERVER_URL + "/unregister";
		Map<String, String> params = new HashMap<String, String>();
		params.put("regId", regId);
		try {
			post(serverUrl, params);
			GCMRegistrar.setRegisteredOnServer(context, false);
			String message = context.getString(R.string.server_unregistered);
			CommonUtilities.displayMessage(context, message);
		} catch (IOException e) {
			// At this point the device is unregistered from GCM, but still
			// registered in the server.
			// We could try to unregister again, but it is not necessary:
			// if the server tries to send a message to the device, it will get
			// a "NotRegistered" error message and should unregister the device.
			String message = context.getString(R.string.server_unregister_error,
					e.getMessage());
			CommonUtilities.displayMessage(context, message);
		}
	}

	/*
	 * POsts the Feedback back to Server
	 */
	public static void sendFeedback(int issue_id, int outcome, String comment, String username) throws Exception
	{
		Log.i(TAG, "Sending feedback on issue (issueId = " + issue_id + ")");
		String serverUrl = DEFAULT_SERVER_URL + "/FeedbackServlet";

		Map<String, String> params = new HashMap<String, String>();
		params.put("issueID", Integer.toString(issue_id));
		params.put("outcome", Integer.toString(outcome));
		params.put("comment", comment);
		params.put("username", username);

		post(serverUrl, params);
	}

	@SuppressWarnings("unused")
	public static void postData(Issue issue, int outcome, String comment, String username) 
	{
		
		class PostTask implements Runnable {

			Issue issue;
			int outcome;
			String comment, username;
			
			PostTask(Issue issue, int outcome, String comment, String username)
			{
				this.issue = issue;
				this.outcome = outcome;
				this.comment = comment;
				this.username = username;
			}
			
			@Override
			public void run() {
				// Create a new HttpClient and Post Header
				HttpClient httpclient = new DefaultHttpClient();
				HttpPost httppost = new HttpPost(DEFAULT_SERVER_URL + "/FeedbackServlet");

				try {
					// Add your data
					List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(2);
					nameValuePairs.add(new BasicNameValuePair("issueID", Integer.toString(issue.getIssueID())));
					nameValuePairs.add(new BasicNameValuePair("temperature", Integer.toString(issue.getTemperature())));
					nameValuePairs.add(new BasicNameValuePair("activitylevel", Integer.toString(issue.getActiviylevel())));
					nameValuePairs.add(new BasicNameValuePair("location", issue.getEpisode()));
					nameValuePairs.add(new BasicNameValuePair("timestamp", issue.getDate().toString()));
					nameValuePairs.add(new BasicNameValuePair("outcome", Integer.toString(outcome)));
					nameValuePairs.add(new BasicNameValuePair("comment", comment));
					nameValuePairs.add(new BasicNameValuePair("username", username));
					httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

					// Execute HTTP Post Request
					HttpResponse response = httpclient.execute(httppost);

				} catch (ClientProtocolException e) {
					// TODO Auto-generated catch block
				} catch (IOException e) {
					// TODO Auto-generated catch block
				}				
			}
		
		}
		
		Thread t = new Thread(new PostTask(issue, outcome, comment, username));
		t.start();
	} 

	/**
	 * Issue a POST request to the server.
	 *
	 * @param endpoint POST address.
	 * @param params request parameters.
	 *
	 * @throws IOException propagated from POST.
	 */
	private static void post(String endpoint, Map<String, String> params)
			throws IOException {
		URL url;
		try {
			url = new URL(endpoint);
		} catch (MalformedURLException e) {
			throw new IllegalArgumentException("invalid url: " + endpoint);
		}
		StringBuilder bodyBuilder = new StringBuilder();
		Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
		// constructs the POST body using the parameters
		while (iterator.hasNext()) {
			Entry<String, String> param = iterator.next();
			bodyBuilder.append(param.getKey()).append('=')
			.append(param.getValue());
			if (iterator.hasNext()) {
				bodyBuilder.append('&');
			}
		}
		String body = bodyBuilder.toString();
		Log.v(TAG, "Posting '" + body + "' to " + url);
		byte[] bytes = body.getBytes();
		HttpURLConnection conn = null;
		try {
			conn = (HttpURLConnection) url.openConnection();
			conn.setDoOutput(true);
			conn.setUseCaches(false);
			conn.setFixedLengthStreamingMode(bytes.length);
			conn.setRequestMethod("POST");
			conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
			// post the request
			OutputStream out = conn.getOutputStream();
			out.write(bytes);
			out.close();
			// handle the response
			int status = conn.getResponseCode();
			if (status != 200) {
				throw new IOException("Post failed with error code " + status);
			}
		} finally {
			if (conn != null) {
				conn.disconnect();
			}
		}
	}
}
